package lispinterpreter;


/**
 * Esta clase evalua listas con la libreria de funciones que se debian implementar,
 * es decir las que no son definidas por el usuario. Esta clase solo trabaja
 * para listas sin sublistas. A estos operadores les llamaremos elementales.
 * @author JuanFer
 *
 */
public class ElementaryFunctions {

	/**
	 * Evalua una lista simple con operadores elementales.
	 * @param list Lista simple a evaluar.
	 * @return LispList con el valor evaluado(LispAtom).
	 */
    public static LispList evaluate(LispList list)  {

	if (list.atomVerifier())
	    return list;
	else {
	    LispList result = null;
	    LispAtom atom = (LispAtom) list.getFirstElement();
	    String operator = atom.satom;
	    if (atom.getType() == LispAtom.ATOM_TYPE_STRING) {
		if (operator.equals("+"))
		    result = sum(list);
		else if (operator.equals("-"))
		    result = sub(list);
		else if (operator.equals("/"))
		    result = div(list);
		else if (operator.equals("*"))
		    result = mult(list);
		else if (operator.equals("<"))
		    result = lessThan(list);
		else if (operator.equals(">"))
		    result = greaterThan(list);
		else if (operator.equals("="))
		    result = equals(list);
		else if (operator.equals("<=")) {
		    result = lessThanEquals(list);
		} else if (operator.equals(">=")) {
		    result = greaterThanEquals(list);
		}
		else if (operator.equals("eq"))
		    result = equivalent(list);
		else
		    System.err.println("Operator not valid.");
	    } else
		System.err.println("Parameter missing.");

	    return result;
	}
    }
    
    /**
     * Suma los valores de list y verifica que sea esta la operacion.
     * @param list Lista a evaluar.
     * @return LispList con la Suma de los valores de la lista.
     */
    private static LispList sum(LispList list) {
	if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
	    System.err.print(
		    "Wrong parameter handed, required float. sum");
	}
	int len = list.size();
	LispAtom atom;
	float result = 0;
	for (int i = 1; i < len; i++) {
	    atom = (LispAtom) list.getElement(i);
	    result = result + atom.fatom;
	}
	atom = new LispAtom(result);
	return atom;
    }

    /**
     * Resta los valores de list y verifica que sea esta la operacion.
     * @param list Lista a evaluar.
     * @return LispList con la resta de los valores de la lista.
     */
    private static LispList sub(LispList list) {
	if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
	    System.err.print(
		    "Wrong parameter handed, required float. sub");
	}
	int len = list.size();
	LispAtom atom;
	float result = 0;
	for (int i = 1; i < len; i++) {
	    atom = (LispAtom) list.getElement(i);
	    if (i == 1)
		result = atom.fatom;
	    else
		result = result - atom.fatom;
	}
	atom = new LispAtom(result);
	return atom;
    }

    /**
     * multiplica los valores de list y verifica que sea esta la operacion.
     * @param list Lista a evaluar.
     * @return LispList con la multiplicacion de los valores de la lista.
     */
    private static LispList mult(LispList list) {
	if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT)) {
	    System.err.print(
		    "Wrong parameter handed, required float. mult");
	}
	int len = list.size();
	LispAtom atom;
	float result = 1;
	for (int i = 1; i < len; i++) {
	    atom = (LispAtom) list.getElement(i);
	    result = result * atom.fatom;
	}
	atom = new LispAtom(result);
	return atom;
    }

    /**
     * Divide los valores de list y verifica que sea esta la operacion.
     * @param list Lista a evaluar.
     * @return LispList con la division de los valores de la lista.
     */
    private static LispList div(LispList list) {
	if (!list.isParameter(0)) {
	    System.err.print(
		    "Wrong parameter handed, required float. div");
	}
	int len = list.size();
	LispAtom atom;
	float result = 1;
	for (int i = 1; i < len; i++) {
	    atom = (LispAtom) list.getElement(i);
	    if (i == 1)
		result = atom.fatom;
	    else
		result = result / atom.fatom;
	}
	atom = new LispAtom(result);
	return atom;
    }

    /**
     * Dice si todos los valores de la lista son igualesy verifica que sea esta la operacion.
     * @param list Lista a evaluar.
     * @return Verdadero si todos los elementos son iguales.
     */
    private static LispList equals(LispList list) {
	if (!list.isParameter(0)) {
	    System.err.print(
		    "Wrong parameter handed, required float. =");
	}
	int len = list.size();
	LispAtom atom1;
	LispAtom atom2;
	boolean result = true;
	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);
	    result = result && (atom1.fatom == atom2.fatom);
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    /**
     * Dice si cada elemento de la lista son el mismo atomo.
     * @param list
     * @return Verdadero si son iguales.
     */
    private static LispList equivalent(LispList list) {
	int len = list.size();
	boolean result = true;
	LispAtom atom = (LispAtom) list.getElement(1);
	int type = atom.getType();

	LispAtom atom1;
	LispAtom atom2;

	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);

	    switch (type) {
	    // float
	    case LispAtom.ATOM_TYPE_FLOAT:
		if (!list.isParameter(LispAtom.ATOM_TYPE_FLOAT))
		    result = false;
		result = result && (atom1.fatom == atom2.fatom);
		break;
	    // boolean
	    case LispAtom.ATOM_TYPE_BOOLEAN:
		if (!list.isParameter(LispAtom.ATOM_TYPE_BOOLEAN))
		    result = false;
		result = result && (atom1.batom == atom2.batom);
		break;
	    // string
	    case LispAtom.ATOM_TYPE_STRING:
		if (!list.isParameter(LispAtom.ATOM_TYPE_STRING))
		    result = false;
		result = result && (atom1.satom.equals(atom2.satom));
		break;
	    }
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    /**
     * Compara los numeros dentro de la lista.
     * @param list
     * @return Verdadero si el elemento a la izquierda es menor al elemento a la derecha
     * para cada dos elementos de la lista.
     */
    private static LispList lessThan(LispList list) {
	if (!list.isParameter(0)) {
	    System.err.print(
		    "Wrong parameter handed, required float. lessThan");
	}
	int len = list.size();
	LispAtom atom1;
	LispAtom atom2;
	boolean result = true;
	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);
	    result = result && (atom1.fatom < atom2.fatom);
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    /**
     * Compara los numeros dentro de la lista.
     * @param list
     * @return Verdadero si el elemento a la izquierda es mayor al elemento a la derecha
     * para cada dos elementos de la lista.
     */
    private static LispList greaterThan(LispList list) {
	int len = list.size();
	LispAtom atom1;
	LispAtom atom2;
	boolean result = true;
	for (int i = 1; i < len - 1; i++) {
	    atom1 = (LispAtom) list.getElement(i);
	    atom2 = (LispAtom) list.getElement(i + 1);
	    result = result && (atom1.fatom > atom2.fatom);
	}
	atom1 = new LispAtom(result);
	return atom1;
    }

    /**
     * Compara los numeros dentro de la lista.
     * @param list
     * @return Verdadero si el elemento a la izquierda es menor o igual al elemento a la derecha
     * para cada dos elementos de la lista.
     */
    private static LispList lessThanEquals(LispList list) {
	boolean result = true;
	int len = list.size();
	float atom1;
	float atom2;
	
	for (int i = 1; i < len -1; i++) {
	    atom1 = ((LispAtom) list.getElement(i)).fatom;
	    atom2 = ((LispAtom) list.getElement(i + 1)).fatom;
	    if (atom1 < atom2 || atom1 == atom2) {
		result = true;
	    } else
		result = false;
	}
	LispAtom atom = new LispAtom(result);
        return atom;
    }
    
    /**
     * Compara los numeros dentro de la lista.
     * @param list
     * @return Verdadero si el elemento a la izquierda es mayor o igual al elemento a la derecha
     * para cada dos elementos de la lista.
     */
    private static LispList greaterThanEquals(LispList list) {
	boolean result = true;
	int len = list.size();
	float atom1;
	float atom2;
	
	for (int i = 1; i < len -1; i++) {
	    atom1 = ((LispAtom) list.getElement(i)).fatom;
	    atom2 = ((LispAtom) list.getElement(i + 1)).fatom;
	    if (atom1 > atom2 || atom1 == atom2) {
		result = true;
	    } else
		result = false;
	}
	LispAtom atom = new LispAtom(result);
        return atom;
    }
}
